using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using CriticalSystems.Instrumentation.Internal;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Win32;

namespace CriticalSystems.Instrumentation
{
    /// <summary>
    /// MSBuild task that creates an event log resource file.
    /// </summary>
    public class SystemInformationTask : Task
    {
        private string momPack = string.Empty;

        [Required]
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
            public ITaskItem[] Systems { get; set; }
// ReSharper restore UnusedAutoPropertyAccessor.Global
// ReSharper restore MemberCanBePrivate.Global

        [Required]
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
            public string ProjectPath { get; set; }
// ReSharper restore UnusedAutoPropertyAccessor.Global
// ReSharper restore MemberCanBePrivate.Global

// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
        public bool GenerateMomPack { get; set; }
// ReSharper restore UnusedAutoPropertyAccessor.Global
// ReSharper restore MemberCanBePrivate.Global

        // ReSharper disable MemberCanBePrivate.Global
        // ReSharper disable UnusedAutoPropertyAccessor.Global
        public string SdkPath { get; set; }
        // ReSharper restore UnusedAutoPropertyAccessor.Global
        // ReSharper restore MemberCanBePrivate.Global
        
        [Output]
// ReSharper disable MemberCanBePrivate.Global
        public string Win32Resources { get; private set; }
// ReSharper restore MemberCanBePrivate.Global

        /// <summary>
        /// When overridden in a derived class, executes the task.
        /// </summary>
        /// <returns>
        /// true if the task successfully executed; otherwise, false.
        /// </returns>
        public override bool Execute()
        {
            bool success = true;
            try
            {
                Log.LogMessage("Starting");
                Win32Resources = string.Empty;
                foreach (ITaskItem systemItem in Systems)
                {
                    string systemInfoPath = Path.Combine(ProjectPath, systemItem.ItemSpec);
                    Log.LogMessage("Processing system {0}", systemInfoPath);
                    IXPathNavigable systemInfoXml;
                    using (
                        var systemInfoStream = new FileStream(systemInfoPath, FileMode.Open, FileAccess.Read,
                                                              FileShare.Read))
                    {
                        XmlReader validatingReader =
                            SchemaValidationHelper.GetValidatingReader(SchemaValidationHelper.SystemInformationSchema,
                                                                       systemInfoStream);
                        systemInfoXml = new XPathDocument(validatingReader);
                    }
                    XPathNavigator navigator = systemInfoXml.CreateNavigator();
                    navigator.MoveToFirstChild();
                    string win32Resource = GetWin32ResourcePath(systemItem.ItemSpec);
                    if (Win32Resources != string.Empty)
                    {
                        Win32Resources += ",";
                    }
                    Win32Resources += win32Resource;
                    string system = navigator.GetAttribute("name", string.Empty);
                    Log.LogMessage("Loaded system information XML for system {0}", system);
                    var transform = new XslCompiledTransform();
                    IXPathNavigable xslt =
                        new XPathDocument(
                            ResourceUtilities.GetEmbeddedResource(
                                "CriticalSystems.Instrumentation.CreateSystemInformationMessages.xslt"));
                    transform.Load(xslt);
                    Log.LogMessage("Loaded message transform");
                    using (var output = new StreamWriter(GetMessagesPath(systemItem.ItemSpec), false, Encoding.ASCII))
                    {
                        transform.Transform(systemInfoXml, null, output);
                    }
                    Log.LogMessage("Written message file");

                    if (!File.Exists(GetMcCommandLine()))
                    {
                        Log.LogError("Can't find mc tool '{0}'", GetMcCommandLine());
                        success = false;
                    }
                    else
                    {
                        success = RunProcess(GetMcCommandLine(), GetMessagesPath(systemItem.ItemSpec));
                        if (success)
                        {
                            Log.LogMessage("Ran mc process");
                            success = RunProcess(GetRcCommandLine(),
                                                 string.Format(CultureInfo.InvariantCulture, "/r {0}",
                                                               GetResourcePath(systemItem.ItemSpec)));
                            if (success)
                            {
                                Log.LogMessage("Ran rc process");
                            }
                        }
                    }
                    if (success && GenerateMomPack)
                    {
                        momPack = GetMomPackPath(system);
                        Log.LogMessage("Generating MOM pack {0}", momPack);
                        transform = new XslCompiledTransform();
                        xslt = new XPathDocument(
                            ResourceUtilities.GetEmbeddedResource("CriticalSystems.Instrumentation.CreateMomPack.xslt"));
                        transform.Load(xslt);
                        Log.LogMessage("Loaded MOM transform");
                        using (var output = new StreamWriter(momPack, false, Encoding.UTF8))
                        {
                            transform.Transform(systemInfoXml, null, output);
                        }
                        Log.LogMessage("Written MOM pack file {0}", momPack);
                    }
                }
            }
            catch (XmlException xex)
            {
                Log.LogErrorFromException(xex, true);
                success = false;
            }
            return success;
        }

        private string GetToolsPath()
        {
            string sdkPath = SdkPath;
            if (string.IsNullOrEmpty(sdkPath))
            {
                RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows",
                                                                      false);
                if (regKey == null)
                {
                    throw new ConfigurationErrorsException("Windows SDK needs to be installed");
                }
                using (regKey)
                {
                    string currentVersion = regKey.GetValue("CurrentVersion").ToString();
                    Log.LogMessage("Current version {0}", currentVersion);
                    var subKey = regKey.OpenSubKey(currentVersion);
                    string installPath = subKey.GetValue("InstallationFolder").ToString();
                    Log.LogMessage("Installation path {0}", installPath);
                    //var installPath = @"C:\Program Files\Microsoft SDKs\Windows\v6.0\";
                    sdkPath = Path.Combine(installPath, @"bin");
                }
            }
            return sdkPath;
        }

        private string GetMcCommandLine()
        {
            return string.Format(CultureInfo.InvariantCulture, @"{0}\mc.exe", GetToolsPath());
        }

        private string GetRcCommandLine()
        {
            return string.Format(CultureInfo.InvariantCulture, @"{0}\rc.exe", GetToolsPath());
        }

        private static string GetResourcePath(string systemFile)
        {
            return string.Format(CultureInfo.InvariantCulture, "{0}.rc", Path.GetFileNameWithoutExtension(systemFile));
        }

        private static string GetMessagesPath(string systemFile)
        {
            return string.Format(CultureInfo.InvariantCulture, "{0}.mc", Path.GetFileNameWithoutExtension(systemFile));
        }

        private static string GetWin32ResourcePath(string systemFile)
        {
            return string.Format(CultureInfo.InvariantCulture, "{0}.res", Path.GetFileNameWithoutExtension(systemFile));
        }

        private static string GetMomPackPath(string system)
        {
            return string.Format(CultureInfo.InvariantCulture, "{0}.Pack.xml", system);        }

        private bool RunProcess(string fileName, string arguments)
        {
            bool success = true;
            var process = new Process
                              {
                                  StartInfo =
                                      {
                                          FileName = fileName,
                                          Arguments = arguments,
                                          CreateNoWindow = true,
                                          UseShellExecute = false,
                                          RedirectStandardError = true
                                      }
                              };
            process.Start();

            StreamReader errorReader = process.StandardError;
            process.WaitForExit();
            int exitCode = process.ExitCode;
            if (exitCode != 0)
            {
                Log.LogMessagesFromStream(errorReader, MessageImportance.High);
                success = false;
            }
            return success;
        }
    }
}